package web

import (
	"io"
	"log"
	"mime/multipart"
	"net/http"
	"os"
	"path/filepath"
)

type FileUploader struct {
	FileField string
	DstPathFunc func(*multipart.FileHeader) string
}

func (u FileUploader) Handle() HandleFunc {
	// 这里可以额外做一些检测
	// if f.FileField == "" {
	// 	// 这种方案默认值我其实不是很喜欢
	// 	// 因为我们需要教会用户说，这个 file 是指什么意思
	// 	f.FileField = "file"
	// }
	return func(ctx *Context) {
		//上传文件的逻辑在这
		file, fileHeader, err := ctx.Req.FormFile(u.FileField)
		if err != nil{
			ctx.RespStatusCode = 500
			ctx.RespData = []byte("上传失败")
			return
		}
		defer file.Close()

		//将目标文件保存目录计算逻辑交给用户
		dst := u.DstPathFunc(fileHeader)

		os.MkdirAll(filepath.Dir(dst), os.ModeDir)


		dstFile, err := os.OpenFile(dst, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0o666)
		if err != nil{
			ctx.RespStatusCode = 500
			ctx.RespData = []byte("上传失败")
			return
		}
		// buf 会影响性能
		//你要考虑服用
		_, err = io.CopyBuffer(dstFile, file, nil)
		if err != nil{
			ctx.RespStatusCode = 500
			ctx.RespData = []byte("上传失败")
			return
		}

		ctx.RespStatusCode = http.StatusOK
		ctx.RespData = []byte("上传成功")

	}
}


// HandleFunc 这种设计方案也是可以的，但是不如上一种灵活。
// 它可以直接用来注册路由
// 上一种可以在返回 HandleFunc 之前可以继续检测一下传入的字段
// 这种形态和 Option 模式配合就很好
func (f *FileUploader) HandleFunc(ctx *Context) {
	src, srcHeader, err := ctx.Req.FormFile(f.FileField)
	if err != nil {
		ctx.RespStatusCode = 400
		ctx.RespData = []byte("上传失败，未找到数据")
		log.Fatalln(err)
		return
	}
	defer src.Close()
	dst, err := os.OpenFile(f.DstPathFunc(srcHeader),
		os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o666)
	if err != nil {
		ctx.RespStatusCode = 500
		ctx.RespData = []byte("上传失败")
		log.Fatalln(err)
		return
	}
	defer dst.Close()

	_, err = io.CopyBuffer(dst, src, nil)
	if err != nil {
		ctx.RespStatusCode = 500
		ctx.RespData = []byte("上传失败")
		log.Fatalln(err)
		return
	}
	ctx.RespData = []byte("上传成功")
}